package com.nit.netty.handler;

import com.alibaba.fastjson.JSON;
import com.nit.netty.model.Request;
import com.nit.netty.model.Response;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;

/**
 * @Author: whb
 * @Description: 标注一个handler可以被多个channel共享
 * @CreateTime: 2020/8/21 13:38
 */
@ChannelHandler.Sharable
@Slf4j
public class NettyClientHandler extends AbstractNettyHandler{

    private final ConcurrentHashMap<String, SynchronousQueue<Object>> queueMap = new ConcurrentHashMap<>();

    public NettyClientHandler() {
    }

    /**
    * @Description: 断开连接的重连操作不写在这里, 等channel被重新利用的时候会重连
    */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        super.channelInactive(ctx);
        ctx.channel().close();
    }

    /**
    * @Description: 服务返回信息在这里读取
    */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Response response = JSON.parseObject(msg.toString(), Response.class);
        String requestId = response.getRequestId();
        SynchronousQueue<Object> queue = queueMap.get(requestId);
        queue.put(response);
        queueMap.remove(requestId);
    }

    /**
    * @Description: 多个线程任务共用一个channel, 然后回到线程本身queue.take的时候阻塞等待返回
    */
    public SynchronousQueue<Object> sendRequest(Request request, Channel channel) {
        SynchronousQueue<Object> queue = new SynchronousQueue<>();
        // 每个进入的线程都有独特的id
        queueMap.put(request.getId(), queue);
        // 发送信息, 这里不阻塞
        channel.writeAndFlush(request);
        return queue;
    }

    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        log.info("发送心跳消息...");
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {
                Request request = new Request();
                request.setMethodName("heartBeat");
                ctx.channel().writeAndFlush(request);
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    // TODO 异常中断的情况尝试重连
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("通信服务器发生异常." + cause);
        ctx.channel().close();
    }

}
